package wntransform.transformation;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 *
 * @author Jakub Barbierik
 */
public class PLToCZTransformation implements Transformation {


    @Override
    public void transformXML(String inputXMLPath, String outputXMLPath, String xslPath) throws ParserConfigurationException, SAXException, IOException, TransformerConfigurationException, TransformerException{

        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document docIn = db.parse(new File(inputXMLPath));
        Document docOut = db.newDocument();

        //Lexical unit IDs mapped to literals(lexical-units) which are represented by LiteralObjects (literal, sense, pos)
        Map<Long,LiteralObject> literals = new HashMap<Long,LiteralObject>();

        NodeList lexicalUnits = docIn.getElementsByTagName("lexical-unit");

        for (int i = 0; i < lexicalUnits.getLength(); i++) {
            Node lexicalUnit = lexicalUnits.item(i);
            NamedNodeMap attributes = lexicalUnit.getAttributes();
            Node literal = attributes.getNamedItem("name");
            Node sense = attributes.getNamedItem("variant");
            Node pos = attributes.getNamedItem("pos");
            Node id = attributes.getNamedItem("id");

            LiteralObject lU = new LiteralObject(literal.getTextContent(), Integer.parseInt(sense.getTextContent()), pos.getTextContent());
            literals.put( Long.parseLong(id.getTextContent()),lU);
        }
        //Relation type IDs mapped to word representation (name)
        Map<String,String> typeIdToName = new HashMap<String, String>();

        NodeList realationTypes = docIn.getElementsByTagName("relationtypes");
        for(int i = 0; i < realationTypes.getLength(); i++){
            typeIdToName.put(realationTypes.item(i).getAttributes().getNamedItem("id").getTextContent(),
                    realationTypes.item(i).getAttributes().getNamedItem("name").getTextContent());
        }
        //Each child synset ID mapped to set of it's parents; each parent represented by ILRParentObject (id, type of relation)
        Map<String,Set<ILRParentObject>> childToParents = new HashMap<String, Set<ILRParentObject>>();

        NodeList synRelations = docIn.getElementsByTagName("synsetrelations");
        for(int i = 0; i < synRelations.getLength(); i++){
            String parentId = synRelations.item(i).getAttributes().getNamedItem("parent").getTextContent();
            String childId = synRelations.item(i).getAttributes().getNamedItem("child").getTextContent();
            String type = synRelations.item(i).getAttributes().getNamedItem("relation").getTextContent();
            ILRParentObject parent = new ILRParentObject(parentId, typeIdToName.get(type));
            if(!(childToParents.containsKey(childId))){
                Set<ILRParentObject> parents = new HashSet<ILRParentObject>();
                parents.add(parent);
                childToParents.put(childId,parents);
            } else {
                Set<ILRParentObject> parents = childToParents.get(childId);
                parents.add(parent);
                childToParents.put(childId,parents);
            }
        }
        //creating root element
        Element root = docOut.createElement("root");
        docOut.appendChild(root);
        //Each synset ID mapped to synset node (used when creating ILR; need to get to synset when knowing it's ID)
        Map<String, Node> synIdToSynset = new HashMap<String, Node>();

        NodeList synsets = docIn.getElementsByTagName("synset");
        for(int i = 0; i < synsets.getLength(); i++){
            synIdToSynset.put(synsets.item(i).getAttributes().getNamedItem("id").getTextContent(), synsets.item(i));
        }

        //Transformation, creating docOut, tree structure of elements
        for(int i = 0; i < synsets.getLength(); i++){
            if(synsets.item(i).hasChildNodes()){
                Element synset = docOut.createElement("SYNSET");
                Element synId = docOut.createElement("ID");
                Element pos = docOut.createElement("POS");
                Element synonym = docOut.createElement("SYNONYM");
                Element def = docOut.createElement("DEF");

                //converting polish part of speech (pos) to czech format representation
                String synPos = (literals.get(Long.parseLong(synsets.item(i).getChildNodes().item(1).getTextContent()))).getPos();
                if(synPos.equals("przymiotnik")){
                    synPos = "a";
                } else if (synPos.equals("rzeczownik")){
                    synPos = "n";
                } else if (synPos.equals("czasownik")){
                    synPos = "v";
                }  else if (synPos.equals("przysłówek")){
                    synPos = "b";
                } else {
                    synPos = "x"; // unknown
                }

                String sId = String.format("%08d",Long.parseLong(synsets.item(i).getAttributes().getNamedItem("id").getTextContent()));

                synId.setTextContent("ENG30-" + sId + "-" + synPos);
                synset.appendChild(synId);

                pos.setTextContent(synPos);
                synset.appendChild(pos);

                //creating element SYNONYM which consist of elements LITERAL which includes SENSE of words
                for(int j = 0; j < synsets.item(i).getChildNodes().getLength(); j++){
                    String litId = synsets.item(i).getChildNodes().item(j).getTextContent().trim();
                    if(!(litId.equals(""))){
                        Element literal = docOut.createElement("LITERAL");
                        Element sense = docOut.createElement("SENSE");
                        LiteralObject literalObj = literals.get(Long.parseLong(litId));
                        literal.setTextContent(literalObj.getLiteral());
                        sense.setTextContent(Integer.toString(literalObj.getSense()));
                        literal.appendChild(sense);
                        synonym.appendChild(literal);
                    }
                }
                synset.appendChild(synonym);

                //creating ILR elements
                Set<ILRParentObject> parents = childToParents.get(synsets.item(i).getAttributes().getNamedItem("id").getTextContent());
                if(parents != null){
                    for(ILRParentObject p: parents){
                        Element ILR = docOut.createElement("ILR");
                        Element type = docOut.createElement("TYPE");
                        type.setTextContent(p.getType());

                        Node parentSynset = synIdToSynset.get(p.getId());
                        String parentPos = "unknown";
                        if(parentSynset.hasChildNodes()){
                            parentPos = (literals.get(Long.parseLong(parentSynset.getChildNodes().item(1).getTextContent()))).getPos();
                        }
                        if (parentPos.equals("przymiotnik")) {
                            parentPos = "a";
                        } else if (parentPos.equals("rzeczownik")) {
                            parentPos = "n";
                        } else if (parentPos.equals("czasownik")) {
                            parentPos = "v";
                        } else if (parentPos.equals("przysłówek")) {
                            parentPos = "b";
                        } else {
                            parentPos = "x"; // unknown
                        }

                        String pId = String.format("%08d", Long.parseLong(p.getId()));                        
                        ILR.appendChild(type);
                        Node text = docOut.createTextNode(pId);
                        text.setTextContent("ENG30-" + pId + "-" + parentPos);
                        ILR.appendChild(text);
                        synset.appendChild(ILR);
                    }
                }

                def.setTextContent(synsets.item(i).getAttributes().getNamedItem("definition").getTextContent());
                synset.appendChild(def);

                root.appendChild(synset);

            }
        }

        Source src = new DOMSource(docOut);
        Result res = new StreamResult(new File(outputXMLPath));

        //using XSL stylesheet to get rid of root element (not part of czech WordNet format)
        StreamSource xslStream = new StreamSource(xslPath);

        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer trans = tf.newTransformer(xslStream);
        trans.transform(src, res);
    }
}
